fix(octopus): split IOG slots at the daily low-rate cap boundary - #4644
Conversation
load_octopus_slots() previously flipped a whole multi-block dispatch slot to the max rate the moment it crossed the octopus_slot_max daily cap, rather than costing only the blocks beyond the cap at the max rate. This overstated cost/metric whenever a dispatch straddled the boundary (batpred#4624). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟢 Approval recommended
The change directly addresses the reported incorrect repricing behaviour and includes targeted test coverage validating the corrected split-slot costing logic.
Pull request overview
Fixes Octopus Intelligent Go (IOG) car-charging cost accounting by correctly splitting a single dispatch slot when it crosses the daily low-rate block cap, so only the portion beyond the cap is priced at rate_max_base (instead of repricing the entire slot).
Changes:
- Updated
load_octopus_slots()to split partially-over-cap dispatch slots into low-rate and max-rate chunks at the exact cap boundary. - Extended the Octopus slot unit tests to assert correct split-slot behaviour and to decouple the overlap/containment test from the low-rate cap logic.
File summaries
| File | Description |
|---|---|
| apps/predbat/octopus.py | Splits IOG slots at the remaining low-rate block boundary and apportions kWh/cost across the resulting chunks. |
| apps/predbat/tests/test_octopus_slots.py | Adds an expected split-slot case for the cap boundary and isolates the containment-overlap test from cap side-effects. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
dp2() on both sides of the cap-boundary split let low_kwh + high_kwh drift from the original kwh via double-rounding, and made the split path's kwh precision inconsistent with this function's non-split chunks, which already carry full-precision kwh (dp2() only applied to cost/soc downstream). Kept full precision for the split kwh values instead, matching the rest of the function. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Addressed in 2e9f191: kept full precision for the split kWh values instead of rounding both sides with dp2() - that double-rounding could drift low_kwh + high_kwh from the original kwh, and was inconsistent with this function's non-split chunks, which already carry full-precision kwh (dp2() only applied to cost/soc downstream). |
Summary
load_octopus_slots()(car-charging cost accounting) previously flipped a whole multi-block Octopus Intelligent Go dispatch slot torate_max_basethe moment it crossed theoctopus_slot_maxdaily low-rate cap, instead of only pricing the blocks beyond the cap at the higher rate. A single overnight dispatch straddling the cap boundary got fully re-priced at the peak rate instead of being split.Correctness note (not a change in this PR)
On IOG, the car's own 6-hour low-rate allowance is independent of the house's rate - the house still gets the cheap night rate (or a dynamic daytime slot) after the car's cap is used up, while the car's own excess charging reverts to the standard/peak rate regardless of time of day.
rate_add_io_slots()(the general "Import p" plan column) already reflects this correctly - it leaves the real underlying tariff rate untouched once the cap is hit, rather than overwriting it.load_octopus_slots()correctly falls back torate_max_basefor the car's own excess kWh, matching that real tariff behaviour, unchanged by this PR.The one known gap: the plan's shared "Import p" column and tooltips only ever show the house rate for a given time-slot, so when the car is paying the peak rate for excess charging during a period the house shows as cheap, that divergence isn't visible in the UI. That's a display-only follow-up, not a calculation bug, and is being tracked separately rather than folded into this fix.
Test plan
./run_all --quickpasses (including theoctopus_slotssuite and the full random-scenario regression baseline)run_pre_commitcleandebug.yamlreplay: plan metric drops from 1226.5736 to 799.8496 with per-slot car charging costs now matching the real dispatch rate🤖 Generated with Claude Code